<?php

namespace App\Services\AliOss\OSS;

class OssStsService
{
    /**
     * @var array
     */
    protected $config = [
        'host'            => 'https://sts.aliyuncs.com',
        'acessKeyID'      => '',
        'accessKeySecret' => '',
        'roleArn'         => '',
        'policy'          => '',
        'roleSessionName' => '',
        'bucket'          => '',
        'endpoint'        => '',
        'timeout'         => 3600,
    ];
    protected $credentials = [];

    public function __construct(array $config)
    {
        $this->config = array_merge($this->config, $config);
    }

    /**
     * https://help.aliyun.com/document_detail/28763.html
     * @param string $roleSessionName
     * @param int $durationSeconds
     * @return string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    protected function assumeRole(int $timeout = 3600)
    {
        $params = array_merge($this->publicParams(), [
            'Action'          => 'AssumeRole',
            'RoleArn'         => $this->config['roleArn'],
            'RoleSessionName' => $this->config['roleSessionName'],
            'DurationSeconds' => $timeout ?: $this->config['timeout']
        ]);
        if (!empty($this->config['policy'])) {
            $params['Policy'] = $this->config['policy'];
        }
        $params['Signature'] = $this->generateSign($params, $this->config['accessKeySecret']);;
        return $this->get($this->config['host'], $params);
    }

    protected function getCredentials($timeout = 3600)
    {
        try {
            $res = $this->assumeRole($timeout);
            $Credentials = data_get(json_decode($res, true), 'Credentials', []);
            return $Credentials;
        } catch (\Exception $exception) {
            throw new \Exception('oss sts connect fail');
        }
    }

    public function getPreviewUrl($object, int $timeout = 0)
    {
        $options = array("response-content-disposition" => "inline",);
        return $this->signUrl($object, $options, $timeout);
    }

    public function getDownloadUrl($object, int $timeout = 0)
    {
        $options = array("response-content-disposition" => "attachment");
        return $this->signUrl($object, $options, $timeout);
    }

    protected function signUrl($object, $options, int $timeout = 0)
    {
        //dump( url()->isValidUrl('http://oss-cn-hangzhou.aliyuncs.com'))
        try {
            $timeout = $timeout ?: $this->config['timeout'];
            $endpoint = $this->config['endpoint'];
            $bucket = $this->config['bucket'];
            $credentials = $this->getCredentials($timeout);
            $accessKeyId = $credentials['AccessKeyId'];
            $accessKeySecret = $credentials['AccessKeySecret'];
            $SecurityToken = $credentials['SecurityToken'];
            if (!url()->isValidUrl($endpoint)) {
                $endpoint = "http://" . $endpoint;
            }
            //dump($endpoint);exit;
            $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false, $SecurityToken);
            return $ossClient->signUrl($bucket, $object, $timeout, 'GET', $options);
        } catch (\Exception $exception) {
            throw new \Exception('oss url sign invalid');
        }
    }


    /**
     * 呼叫着身份
     * https://help.aliyun.com/document_detail/43767.html
     * @return string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    protected function getCallerIdentity()
    {
        $params = array_merge($this->publicParams(), [
            'Action' => 'GetCallerIdentity',
        ]);
        $params['Signature'] = $this->generateSign($params, $this->config['accessKeySecret']);;
        return $this->get($this->config['host'], $params);
    }

    /**
     * https://help.aliyun.com/document_detail/109979.html
     * @param string $SAMLProviderArn
     * @param string $roleArn
     * @param int $durationSeconds
     * @param string $policy
     * @return string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function assumeRoleWithSAML(string $SAMLProviderArn, string $roleArn, int $durationSeconds = 3600, string $policy = '')
    {
        $params = array_merge($this->publicParams(), [
            'Action'          => 'AssumeRoleWithSAML',
            'SAMLProviderArn' => $SAMLProviderArn,
            'RoleArn'         => $roleArn,
            'DurationSeconds' => $durationSeconds
        ]);
        if ($policy) {
            $params['Policy'] = $policy;
        }
        $params['Signature'] = $this->generateSign($params, $this->config['accessKeySecret']);;
        return $this->get($this->config['host'], $params);
    }

    /**
     * 公共请求参数
     * @return array
     */
    protected function publicParams()
    {
        return [
            'Format'           => 'json',
            'Version'          => '2015-04-01',
            'SignatureMethod'  => 'HMAC-SHA1',
            'SignatureNonce'   => uniqid(),
            'SignatureVersion' => '1.0',
            'AccessKeyId'      => $this->config['acessKeyID'],
            'Timestamp'        => gmdate('Y-m-d\TH:i:s\Z'),
        ];
    }

    /**
     * @param string $url
     * @param array $query
     * @param array $headers
     * @return string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    protected function get(string $url, array $query = [], array $headers = [])
    {
        $resp = (new \GuzzleHttp\Client(['verify' => false]))->request('get', $url, [
            'headers' => $headers,
            'query'   => $query,
        ]);
        return $resp->getBody()->getContents();
    }

    /**
     * @param $params
     * @param $accessKeySecret
     * @return string
     */
    protected function generateSign($params, $accessKeySecret)
    {
        ksort($params);
        $stringToSign = 'GET&%2F&' . urlencode(http_build_query($params, null, '&', PHP_QUERY_RFC3986));
        return base64_encode(hash_hmac('sha1', $stringToSign, $accessKeySecret . '&', true));
    }
}
